/// 
/// MaDAson - Toolkit for Model Driven Software Development
/// 
/// Copyright [2006] Marcus Lankenau
/// 
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///  
///     http://www.apache.org/licenses/LICENSE-2.0
///  
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
/// 

using System;
using System.Collections;
using System.Text;
using System.Xml.Serialization;
using Madason.MetaModel.Validation;

namespace Madason.MetaModel
{
	/// <summary>
	/// Class 
	/// </summary>
	[Serializable]
	public class Class : ModelElement
	{
		private Namespace @namespace;
		private AttributeList attributes;
		private AssociationList associations;
		private OperationList operations;
		private string superClassID;
		private ClassList subClasses = null;
		private StateMachineList stateMachines;


		public StateMachineList StateMachines
		{
			get 
			{
				if (stateMachines == null) stateMachines = new StateMachineList();
				return stateMachines; 
			}
			set { stateMachines = value; }
		}
	

		[XmlAttribute("superClass")]
		public string SuperClassID
		{
			get { return superClassID;}
			set { superClassID = value;}
		}



		/// <summary>
		/// returns the super class
		/// </summary>
		[XmlIgnore]
		public Class SuperClass
		{
			get { return Model == null ? null : Model.FindClassById(SuperClassID); }
		}


		/// <summary>
		/// returns all direct subclasses
		/// </summary>
		[XmlIgnore]
		public ClassList SubClasses
		{
			get
			{
				//removed by hefeisla for eleminateInheritance
				//if (subClasses != null) return subClasses;
				subClasses = Model.FindSubClassesFor(this);
				return subClasses;				
			}
		}


		/// <summary>
		/// returns all subclasses and the subclasses of the subclasses and...
		/// </summary>
		[XmlIgnore]
		public ClassList SubClassesDeep
		{
			get
			{
				ClassList result = new ClassList();
				result.AddRange(SubClasses);
				foreach (Class c in SubClasses)
				{
					result.AddRange(c.SubClassesDeep);
				}
				return result;
			}
		}

		/// <summary>
		/// returns a path of superclasses
		/// </summary>
		[XmlIgnore]
		public ClassList SuperClassesPath
		{
			get
			{
				ClassList results = new ClassList();

				if (SuperClass != null)
				{
					results.AddRange(SuperClass.SuperClassesPath);
					results.Add(SuperClass);
				}
				
				return results;
			}
		}

		/// <summary>
		/// is the current class a subclass of of cls
		/// </summary>
		/// <param name="cls"></param>
		/// <returns></returns>
		public bool IsSubClassOf(Class cls)
		{
			if (SuperClassID == cls.Id) return true;
			if (SuperClass == null) return false;
			return SuperClass.IsSubClassOf(cls);
		}

		/// <summary>
		/// full name of class including namespace
		/// </summary>
		[XmlIgnore]
		public string ExplicitName
		{
			get 
			{
				if (Namespace is Model)
					return Name;
				else
					return Namespace.FullName + "." + Name; 
			}
		}

		/// <summary>
		/// Attributes
		/// </summary>
		[XmlElement(typeof(Attribute), ElementName = "attribute")]
		public AttributeList Attributes
		{
			get 
			{
				if (attributes == null)
				{
					attributes = new AttributeList();
					attributes.AfterAttributeAdded += new AttributeAddDelegate(attributes_AfterAttributeAdded);
				}
				return attributes; 
			}
			set { 
				attributes = value;
				attributes.AfterAttributeAdded += new AttributeAddDelegate(attributes_AfterAttributeAdded);			
			}
		}



		/// <summary>
		/// Operations
		/// </summary>
		[XmlElement(typeof(Operation), ElementName = "operation")]
		public OperationList Operations
		{
			get 
			{
				if (operations == null)
				{
					operations = new OperationList();
					operations.AfterOperationAdded += new OperationAddDelegate(operations_AfterOperationAdded);
				}
				return operations; 
			}
			set 
			{ 
				operations = value;
				operations.AfterOperationAdded += new OperationAddDelegate(operations_AfterOperationAdded);
			}
		}

		void operations_AfterOperationAdded(Operation ns)
		{
			ns.Class = this;
		}
	


		/// <summary>
		/// Associations
		/// </summary>
		[XmlElement(typeof(Association), ElementName = "association")]
		public AssociationList Associations
		{
			get 
			{
				if (associations == null)
				{
					associations = new AssociationList();
					associations.AfterAssociationAdded += new AssociationAddDelegate(associations_AfterAssociationAdded);
				}
				return associations; 
			}
			set 
			{ 
				associations = value;
				associations.AfterAssociationAdded += new AssociationAddDelegate(associations_AfterAssociationAdded);
			}
		}

		/// <summary>
		/// returns active 1-n associations
		/// </summary>
		[XmlIgnore]
		public AssociationList ActiveAssociations
		{
			get 
			{
				AssociationList tempList = new AssociationList();
				foreach (Association assoc in Associations)
				{
					if (assoc.IsComplex) continue;

					if (assoc.Cardinality == Cardinality.One && assoc.OtherEnd.Cardinality == Cardinality.One && assoc.IsNavigable == false) continue;

					if (assoc.Name.Trim().Length > 0 || assoc.Cardinality == Cardinality.One)
						tempList.Add(assoc);
				}
				return tempList;
			}
		}

		/// <summary>
		/// returns complex n-n associations
		/// </summary>
		[XmlIgnore]
		public AssociationList ComplexAssociations
		{
			get
			{
				AssociationList tempList = new AssociationList();
				foreach (Association assoc in Associations)
					if (assoc.Name.Trim().Length > 0 && assoc.IsComplex)
						tempList.Add(assoc);
				return tempList;
			}
		}

		void attributes_AfterAttributeAdded(Attribute attr)
		{
			attr.Class = this;
		}

		void associations_AfterAssociationAdded(Association ns)
		{
			ns.Class = this;
		}


		[XmlIgnore]
		public Namespace Namespace
		{
			get { return @namespace; }
			set { @namespace = value; }
		}


		/// <summary>
		/// Access to the Model
		/// </summary>
		[XmlIgnore]
		public Model Model
		{
			get
			{
				return (Namespace == null) ? null : Namespace.Model;
			}
		}

		public Attribute GetAttribute(Stereotype st)
		{
			foreach (Attribute attr in Attributes)
				if (attr.Stereotype == st)
					return attr;
				//added by hefeisla
				if (SuperClassID != null)
					return GetBaseClass().GetAttribute(st);
			return null;
		}

		public Attribute GetAttribute(string name)
		{
			foreach (Attribute attr in Attributes)
				if (attr.Name.ToLower() == name.ToLower())
					return attr;
			return null;
		}

		public virtual Association GetAssociation(string name)
		{
			foreach (Association assoc in Associations)
				if (assoc.Name.ToLower() == name.ToLower())
					return assoc;
			return null;
		}

		//added by hefeisla
		/// <summary>
		/// Methode liefert die Basisklasse einer Klasse. Also falls eine Vererbungshierarchie vorliegt
		/// die oberste Klasse in der Vererbungshierarchie.
		/// </summary>
		/// <returns>Die Basisklasse</returns>
		public Class GetBaseClass()
		{
			if (this.SuperClassID == null)
			{
				return this;
			}
			else
			{
				return this.SuperClass.GetBaseClass();
			}
		}

		public Attribute FindOneByStereotype(Stereotype st)
		{
			if (Attributes.Count > 0)
				return Attributes.FindOneByStereotype(st);
			return GetBaseClass().Attributes.FindOneByStereotype(st);
		}

		public virtual void ValidateRecursive(ValidationIssueList issues)
		{
			this.Validate(issues);
			foreach (Attribute attr in Attributes)
				attr.ValidateRecursive(issues);
			foreach (Operation op in Operations)
				op.ValidateRecursive(issues);
			foreach (Association assoc in Associations)
				assoc.ValidateRecursive(issues);
			foreach (StateMachine sm in StateMachines)
				sm.ValidateRecursive(issues);
		}
	}

	#region Helper Types

	public delegate void ClassAddDelegate(Class ns);


	/// <summary>
	/// List of classes
	/// </summary>
	public class ClassList : ArrayList
	{
		public event ClassAddDelegate AfterClassAdded;


		/// <summary>
		/// Add class
		/// </summary>
		/// <param name="cls"></param>
		public void Add(Class cls)
		{
			base.Add(cls);
			if (AfterClassAdded != null)
				AfterClassAdded(cls);
		}


		/// <summary>
		/// Filter class by a steretype
		/// </summary>
		/// <param name="st"></param>
		/// <returns></returns>
		public ClassList Filter(Stereotype st)
		{
			ClassList list = new ClassList();
			foreach (Class cls in this)
				if (cls.Stereotype == st) list.Add(cls);
			return list;
		}


		/// <summary>
		/// Filter class by a steretype name
		/// </summary>
		/// <param name="st"></param>
		/// <returns></returns>
		public ClassList Filter(string st)
		{
			ClassList list = new ClassList();
			foreach (Class cls in this)
				if (cls.StereotypeName == st) list.Add(cls);
			return list;
		}

		/// <summary>
		/// Access class via its index
		/// </summary>
		/// <param name="i"></param>
		/// <returns></returns>
		public Class this[int i]
		{
			get 
			{				
				return base[i] as Class;
			}
			set
			{
				base[i] = value;
			}
		}

	}

	#endregion
}
